// Modified or written by Lambdascale SRL for inclusion with lambdaj.
// Copyright (c) 2009-2010 Mario Fusco.
// Licensed under the Apache License, Version 2.0 (the "License")

package ch.lambdaj.group;

import java.util.*;

/**
 * Defines how a set of items should be grouped
 * @author Mario Fusco
 */
public abstract class GroupCondition<T> {

	protected final Map<String, T> additionalProperties = new TreeMap<String, T>();
	
	private String alias;

    private GroupComparator<? extends Comparable> groupComparator;

    protected abstract String getGroupName();
    protected abstract Object getGroupValue(Object item);

	protected String getAlias() {
		return alias;
	}

    /**
     * Sets an alias for the groups created using this condition
     * @param alias The alias to be set
     * @return The GroupCondition itself in order to allow a fluent interface
     */
	public GroupCondition as(String alias) {
		this.alias = alias;
		return this;
	}

	protected Collection<String> getAdditionalPropertyNames() {
		return additionalProperties.keySet();
	}

    protected abstract String getAdditionalPropertyValue(String name, Object item);

    <I> GroupItem<I> create(I item, Object key, String keyAsString) {
        GroupItem<I> groupItem = new GroupItem<I>(key, getAlias());
        groupItem.put(getGroupName(), keyAsString);
        for (String propertyName : getAdditionalPropertyNames()) {
            groupItem.put(propertyName, getAdditionalPropertyValue(propertyName, item));
        }
        return groupItem;
    }

    protected String asNotNullString(Object key) {
        return key == null ? "" : key.toString();
    }

    /**
     * Defines how the groups generated by applying this condition should be sorted based on the value assumed
     * on the given argument by the object that is the key for a given group
     * @param sortingArgument The argument defined using the {@link ch.lambdaj.Lambda#on(Class)} method on which the groups have to be sorted
     * @return The GroupCondition itself in order to allow a fluent interface
     */
    public <A extends Comparable<A>> GroupCondition sort(A sortingArgument) {
        groupComparator = new GroupComparator<A>(sortingArgument);
        return this;
    }

    <G> void sortGroup(List<GroupItem<G>> group) {
        if (groupComparator != null) Collections.sort(group, groupComparator);
    }
}
